#include "chartwindow.h"
#include <QVBoxLayout>
#include <QMouseEvent>
#include <QDebug>

ChartWindow::ChartWindow(QWidget *parent) :
    QMainWindow(parent),
    maxPoints(2000),
    dragPointIndex(-1),
    isDragging(false),
    firstDataReceived(false)
{
    setWindowTitle("串口数据可视化");
    setMinimumSize(800, 600);
    resize(800, 700); // 增大窗口默认大小
    
    initChart();
}

ChartWindow::~ChartWindow()
{
    delete series;
    delete axisX;
    delete axisY;
    delete chart;
    // chartView由布局管理，不需要手动删除
}

void ChartWindow::initChart()
{
    // 创建中央部件
    QWidget *centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);
    
    // 创建主布局
    QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
    
    // 初始化图表
    chart = new QChart();
    series = new QLineSeries();
    
    chart->addSeries(series);
    chart->setTitle("串口数据可视化");
    chart->setAnimationOptions(QChart::SeriesAnimations);
    chart->legend()->hide();
    
    // 设置X轴（时间轴）
    axisX = new QDateTimeAxis();
    axisX->setTitleText("时间");
    axisX->setFormat("hh:mm:ss.zzz"); // 设置时间格式，包括毫秒
    axisX->setTickCount(6); // 设置刻度数量
    QDateTime now = QDateTime::currentDateTime();
    axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints))); // 初始范围设置为当前时间到当前时间+maxPoints毫秒
    chart->addAxis(axisX, Qt::AlignBottom);
    series->attachAxis(axisX);
    
    // 设置Y轴（数据轴）
    axisY = new QValueAxis();
    axisY->setTitleText("数据值");
    axisY->setRange(0, 2000); // 从0开始的初始范围
    axisY->setLabelFormat("%.2f"); // 设置Y轴精度为2位小数
    chart->addAxis(axisY, Qt::AlignLeft);
    series->attachAxis(axisY);
    
    // 创建图表视图
    chartView = new QChartView(chart);
    chartView->setRenderHint(QPainter::Antialiasing);
    chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
    // 允许图表交互
    chartView->setRubberBand(QChartView::RectangleRubberBand);
    chartView->setInteractive(true);
    chart->setAcceptHoverEvents(true);
    chartView->setDragMode(QGraphicsView::ScrollHandDrag);
    chartView->setMouseTracking(true);
    chartView->installEventFilter(this);
    
    // 添加图表视图到布局
    mainLayout->addWidget(chartView);
    
    // 初始化控制按钮和采样参数调节控件
    initControls(mainLayout);
}

void ChartWindow::initControls(QVBoxLayout *mainLayout)
{
    // 创建控制按钮布局
    QHBoxLayout *buttonLayout = new QHBoxLayout();
    
    // 创建清除按钮
    clearButton = new QPushButton("清除曲线");
    clearButton->setFixedHeight(30);
    buttonLayout->addWidget(clearButton);
    
    // 创建自动锁定按钮
    autoFitButton = new QPushButton("自动锁定最优视图");
    autoFitButton->setFixedHeight(30);
    buttonLayout->addWidget(autoFitButton);
    
    // 添加按钮布局到主布局
    mainLayout->addLayout(buttonLayout);
    
    // 创建采样参数调节布局
    QHBoxLayout *sampleControlsLayout = new QHBoxLayout();
    
    // 采样点数量调节
    sampleControlsLayout->addWidget(new QLabel("采样点数量:"));
    sampleSizeEdit = new QLineEdit(QString::number(maxPoints));
    sampleSizeEdit->setMaximumWidth(80);
    sampleControlsLayout->addWidget(sampleSizeEdit);
    sampleSizeLabel = new QLabel(QString::number(maxPoints));
    sampleControlsLayout->addWidget(sampleSizeLabel);
    sampleSizeRangeLabel = new QLabel("(范围: 50-500000)");
    sampleSizeRangeLabel->setStyleSheet("color: gray; font-size: 10px;");
    sampleControlsLayout->addWidget(sampleSizeRangeLabel);

    // 采样时间调节
    sampleControlsLayout->addWidget(new QLabel("采样时间范围:"));
    sampleTimeEdit = new QLineEdit("5");
    sampleTimeEdit->setMaximumWidth(80);
    sampleControlsLayout->addWidget(sampleTimeEdit);
    sampleTimeLabel = new QLabel("5 ms");
    sampleControlsLayout->addWidget(sampleTimeLabel);
    sampleTimeRangeLabel = new QLabel("(范围: 1-10000 ms)");
    sampleTimeRangeLabel->setStyleSheet("color: gray; font-size: 10px;");
    sampleControlsLayout->addWidget(sampleTimeRangeLabel);
    
    // 添加采样参数调节布局到主布局
    mainLayout->addLayout(sampleControlsLayout);
    
    // 连接信号和槽
    connect(clearButton, &QPushButton::clicked, this, &ChartWindow::clearData);
    connect(autoFitButton, &QPushButton::clicked, this, &ChartWindow::autoFitView);
    connect(sampleSizeEdit, &QLineEdit::editingFinished, this, [=]() {
        bool ok;
        qint64 value = sampleSizeEdit->text().toLongLong(&ok);
        if (ok && value >= 50) { // 验证输入在有效范围内（下限为50，上限理论上无限制）
            sampleSizeLabel->setText(QString::number(value));
            setMaxPoints(value);
            emit sampleSizeChanged(value);
        } else {
            // 输入无效，恢复之前的值
            sampleSizeEdit->setText(QString::number(maxPoints));
            QMessageBox::warning(this, "输入错误", "采样点数量必须大于等于50");
        }
    });
    connect(sampleTimeEdit, &QLineEdit::editingFinished, this, [=]() {
        bool ok;
        int value = sampleTimeEdit->text().toInt(&ok);
        if (ok && value >= 1 && value <= 10000) { // 验证输入在有效范围内
            sampleTimeLabel->setText(QString::number(value) + " ms");
            setSampleTime(value);
            emit sampleTimeChanged(value);
        } else {
            // 输入无效，恢复之前的值
            sampleTimeEdit->setText("5");
            QMessageBox::warning(this, "输入错误", "采样时间必须在1-10000 ms之间");
        }
    });
}

void ChartWindow::addDataPoint(double x, double y)
{
    // 处理时间戳
    qint64 timestamp;
    if (!firstDataReceived) {
        // 记录第一个数据点的时间
        firstDataTime = QDateTime::currentDateTime();
        firstDataReceived = true;
        
        // 第一个数据点，使用时间戳作为x值
        timestamp = firstDataTime.toMSecsSinceEpoch();
    } else {
        // 后续数据点，使用基于firstDataTime的偏移量计算实际时间戳
        qint64 elapsedMSecs = static_cast<qint64>(x);
        timestamp = firstDataTime.toMSecsSinceEpoch() + elapsedMSecs;
    }
    
    // 添加数据点，使用时间戳作为x值
    series->append(timestamp, y);
    
    // 优化数据点管理和X轴范围控制
    if (series->count() > maxPoints) {
        // 保留最新的maxPoints个数据点
        QList<QPointF> points = series->points();
        points.removeFirst();
        series->replace(points);
    }
    
    // 始终更新X轴范围，显示最新的maxPoints个数据点
    if (series->count() > 0) {
        qint64 currentMaxTimestamp = static_cast<qint64>(series->points().last().x());
        QDateTime currentMaxTime = QDateTime::fromMSecsSinceEpoch(currentMaxTimestamp);
        
        // 对于真正的滚动窗口效果，当有足够多的数据点时，显示最新的maxPoints个
        if (series->count() >= maxPoints) {
            qint64 currentMinTimestamp = static_cast<qint64>(series->points().first().x());
            QDateTime currentMinTime = QDateTime::fromMSecsSinceEpoch(currentMinTimestamp);
            axisX->setRange(currentMinTime, currentMaxTime);
        } else {
            // 数据点较少时，从第一个数据点的时间开始显示
            if (firstDataReceived) {
                axisX->setRange(firstDataTime, currentMaxTime);
            } else {
                // 如果还没有收到数据，使用当前时间范围
                QDateTime now = QDateTime::currentDateTime();
                axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
            }
        }
    } else {
        // 没有数据时，保持初始范围
        QDateTime now = QDateTime::currentDateTime();
        axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
    }
    
    // 不再自动调整Y轴范围，允许用户自由拖动
    // 只有在首次添加数据时调整Y轴范围
    if (series->count() == 1) {
        // 基于数据动态设置Y轴初始范围，不再强制最小值为0
        double yMin = y - 5; // 为第一个点提供一些上下空间
        double yMax = y + 5;
        // 确保最小值不会太低
        if (yMin < 0) yMin = 0; // 仍然保持非负值
        axisY->setRange(yMin, yMax); // 设置初始范围，提供足够空间
    }
}

void ChartWindow::setChartTitle(const QString &title)
{
    chart->setTitle(title);
}

void ChartWindow::setAxisRanges(double minX, double maxX, double minY, double maxY)
{
    // 不再强制X轴最小值为0，允许完整显示指定范围
    // 将double类型的时间戳转换为QDateTime
    qint64 minTimestamp = static_cast<qint64>(minX);
    qint64 maxTimestamp = static_cast<qint64>(maxX);
    QDateTime minTime = QDateTime::fromMSecsSinceEpoch(minTimestamp);
    QDateTime maxTime = QDateTime::fromMSecsSinceEpoch(maxTimestamp);
    axisX->setRange(minTime, maxTime);
    // 根据传入的minY和maxY设置Y轴范围，不再强制最小值为0
    // 仍然保持非负值
    double adjustedMinY = qMax(0.0, minY);
    axisY->setRange(adjustedMinY, maxY);
}

void ChartWindow::clearData()
{
    // 显示确认对话框
    QMessageBox::StandardButton reply;
    reply = QMessageBox::question(this, "确认清除数据", "确定要清除所有数据吗？",
                                 QMessageBox::Yes | QMessageBox::No);
    
    if (reply == QMessageBox::Yes) {
        series->clear();
        
        // 清除数据后重置时间轴相关变量
        firstDataReceived = false;
        
        // 设置X轴范围为当前时间到当前时间+maxPoints毫秒
        QDateTime now = QDateTime::currentDateTime();
        axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
        
        // 保持Y轴范围不变，让用户可以继续使用之前的拖动位置
        
        // 发送信号通知主窗口重置数据计数器
        emit resetDataCounter();
    }
}

void ChartWindow::setMaxPoints(qint64 newMaxPoints)
{
    maxPoints = newMaxPoints;
    
    // 更新X轴范围，保持当前显示逻辑
    if (series->count() > 0) {
        qint64 currentMaxTimestamp = static_cast<qint64>(series->points().last().x());
        QDateTime currentMaxTime = QDateTime::fromMSecsSinceEpoch(currentMaxTimestamp);
        
        // 对于足够多的数据点，调整为滚动窗口效果
        if (series->count() >= maxPoints) {
            // 如果数据点过多，只显示最新的maxPoints个
            qint64 currentMinTimestamp = static_cast<qint64>(series->points().first().x());
            QDateTime currentMinTime = QDateTime::fromMSecsSinceEpoch(currentMinTimestamp);
            axisX->setRange(currentMinTime, currentMaxTime);
        } else {
            // 数据点较少时，从第一个数据点的时间开始显示
            if (firstDataReceived) {
                axisX->setRange(firstDataTime, currentMaxTime);
            } else {
                // 如果还没有收到数据，使用当前时间范围
                QDateTime now = QDateTime::currentDateTime();
                axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
            }
        }
    } else {
        // 没有数据时，设置X轴范围为当前时间到当前时间+maxPoints毫秒
        QDateTime now = QDateTime::currentDateTime();
        axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
    }
}

void ChartWindow::setSampleTime(int sampleTime)
{
    // 实现采样时间的设置逻辑
    sampleTimeLabel->setText(QString::number(sampleTime) + " ms");
}

void ChartWindow::updateControlsSilently(qint64 newMaxPoints, int newSampleTime)
{
    // 暂时断开信号连接，防止循环触发
    disconnect(sampleSizeEdit, &QLineEdit::editingFinished, nullptr, nullptr);
    disconnect(sampleTimeEdit, &QLineEdit::editingFinished, nullptr, nullptr);
    
    // 更新控件值
    sampleSizeEdit->setText(QString::number(newMaxPoints));
    sampleSizeLabel->setText(QString::number(newMaxPoints));
    sampleTimeEdit->setText(QString::number(newSampleTime));
    sampleTimeLabel->setText(QString::number(newSampleTime) + " ms");
    
    // 更新内部状态
    maxPoints = newMaxPoints;
    setSampleTime(newSampleTime);
    
    // 重新连接信号
    connect(sampleSizeEdit, &QLineEdit::editingFinished, this, [=]() {
        bool ok;
        qint64 value = sampleSizeEdit->text().toLongLong(&ok);
        if (ok && value >= 50) { // 验证输入在有效范围内（下限为50，上限理论上无限制）
            sampleSizeLabel->setText(QString::number(value));
            setMaxPoints(value);
            emit sampleSizeChanged(value);
        } else {
            // 输入无效，恢复之前的值
            sampleSizeEdit->setText(QString::number(maxPoints));
            QMessageBox::warning(this, "输入错误", "采样点数量必须大于等于50");
        }
    });
    connect(sampleTimeEdit, &QLineEdit::editingFinished, this, [=]() {
        bool ok;
        int value = sampleTimeEdit->text().toInt(&ok);
        if (ok && value >= 1 && value <= 10000) { // 验证输入在有效范围内
            sampleTimeLabel->setText(QString::number(value) + " ms");
            setSampleTime(value);
            emit sampleTimeChanged(value);
        } else {
            // 输入无效，恢复之前的值
            sampleTimeEdit->setText("1000");
            QMessageBox::warning(this, "输入错误", "采样时间必须在1-10000 ms之间");
        }
    });
}

void ChartWindow::autoFitView()
{
    // 确保有数据点可处理
    if (series->count() == 0) {
        // 没有数据时设置默认范围
        QDateTime now = QDateTime::currentDateTime();
        axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
        axisY->setRange(0, 20);
        return;
    }
    
    // 计算数据的最大值和最小值
    QList<QPointF> points = series->points();
    qint64 minTimestamp = static_cast<qint64>(points[0].x());
    qint64 maxTimestamp = static_cast<qint64>(points[0].x());
    double minY = points[0].y();
    double maxY = points[0].y();
    
    for (const QPointF &point : points) {
        qint64 timestamp = static_cast<qint64>(point.x());
        if (timestamp < minTimestamp) minTimestamp = timestamp;
        if (timestamp > maxTimestamp) maxTimestamp = timestamp;
        if (point.y() < minY) minY = point.y();
        if (point.y() > maxY) maxY = point.y();
    }
    
    // 添加一些边距，使图表看起来更美观
    qint64 xMarginMSecs = (maxTimestamp - minTimestamp) * 0.05;
    double yMargin = (maxY - minY) * 0.1;
    
    // 确保边距不为零
    if (xMarginMSecs < 100) xMarginMSecs = 100; // 至少100毫秒的边距
    if (yMargin < 0.5) yMargin = 0.5;
    
    // 仍然保持非负值
    minY = qMax(0.0, minY);
    
    // 转换为QDateTime对象
    QDateTime minTime = QDateTime::fromMSecsSinceEpoch(minTimestamp);
    QDateTime maxTime = QDateTime::fromMSecsSinceEpoch(maxTimestamp);
    
    // 设置新的坐标轴范围
    if (series->count() >= maxPoints) {
        // 对于足够多的数据点，使用滚动窗口效果
        // 获取第一个数据点的时间戳
        qint64 currentMinTimestamp = static_cast<qint64>(points.first().x());
        QDateTime currentMinTime = QDateTime::fromMSecsSinceEpoch(currentMinTimestamp);
        axisX->setRange(currentMinTime, maxTime.addMSecs(xMarginMSecs));
    } else {
        // 数据点较少时，从第一个数据点的时间开始显示
        if (firstDataReceived) {
            axisX->setRange(firstDataTime, maxTime.addMSecs(xMarginMSecs));
        } else {
            // 如果还没有收到数据，使用当前时间范围
            QDateTime now = QDateTime::currentDateTime();
            axisX->setRange(now, now.addMSecs(static_cast<qint64>(maxPoints)));
        }
    }
    axisY->setRange(0, maxY + yMargin);
}

bool ChartWindow::eventFilter(QObject *obj, QEvent *e)
{
    // 忽略非chartView对象的事件
    if (obj != chartView)
        return false;
    
    // 只有在处理特定事件时才返回true，否则让事件继续传递
    if (e->type() == QEvent::MouseButtonPress) {
        QMouseEvent *event = static_cast<QMouseEvent *>(e);
        if (event->button() == Qt::LeftButton) {
            // 转换鼠标位置到图表坐标系
            QPointF scenePoint = chartView->mapToScene(event->pos());
            QPointF chartPoint = chart->mapFromScene(scenePoint);
            
            // 查找最近的数据点
            int closestPoint = -1;
            double minDistance = 10.0; // 设置一个合适的阈值
            QList<QPointF> points = series->points();
            
            for (int i = 0; i < points.size(); ++i) {
                double distance = qSqrt(qPow(points[i].x() - chartPoint.x(), 2) + 
                                      qPow(points[i].y() - chartPoint.y(), 2));
                if (distance < minDistance) {
                    minDistance = distance;
                    closestPoint = i;
                }
            }
            
            // 如果找到足够近的数据点，开始拖动
            if (closestPoint != -1) {
                dragPointIndex = closestPoint;
                isDragging = true;
                return true; // 处理了事件，阻止默认行为
            }
            // 没有找到足够近的数据点，让事件继续传递，以便ScrollHandDrag功能正常工作
            return false;
        }
    } else if (e->type() == QEvent::MouseMove && isDragging) {
        QMouseEvent *event = static_cast<QMouseEvent *>(e);
        
        // 转换鼠标位置到图表坐标系
        QPointF scenePoint = chartView->mapToScene(event->pos());
        QPointF chartPoint = chart->mapFromScene(scenePoint);
        
        // 更新拖动的数据点
        if (dragPointIndex >= 0 && dragPointIndex < series->points().size()) {
            QList<QPointF> points = series->points();
            points[dragPointIndex] = chartPoint;
            series->replace(points);
        }
        return true; // 正在拖动数据点时处理事件
    } else if (e->type() == QEvent::MouseButtonRelease) {
        isDragging = false;
        dragPointIndex = -1;
        return true; // 处理了事件
    }
    
    // 对于其他所有事件，让它们继续传递给默认处理程序
    return false;
}

void ChartWindow::updateTimeAxisWithoutData()
{
    // 获取当前时间
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qreal xValue = currentDateTime.toMSecsSinceEpoch();
    
    // 如果是第一个数据点，记录开始时间
    if (!firstDataReceived) {
        firstDataTime = currentDateTime;
        firstDataReceived = true;
    }
    
    // 添加0值数据点，实现无数据时图形归零
    if (series) {
        series->append(xValue, 0); // 添加0值数据点
        
        // 保持数据点数量不超过maxPoints
        if (series->count() > maxPoints) {
            series->remove(0);
        }
        
        // 更新X轴范围，让时间轴继续滚动
        if (axisX) {
            QDateTime startDateTime = currentDateTime.addMSecs(-maxPoints); // 显示最近maxPoints毫秒的数据
            axisX->setRange(startDateTime, currentDateTime);
        }
    }
}

